/*
 * Copyright 2016-2019 yoara
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package yhao.infra.web.common.security.encrypt.helper;

import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.DependsOn;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import yhao.infra.apilist.ValidationForm;
import yhao.infra.common.InfraCommonConstants;
import yhao.infra.common.cache.YhaoCache;
import yhao.infra.common.constants.InfraCacheKey;
import yhao.infra.common.spring.SpringBeanPicker;
import yhao.infra.common.util.encrypt.AESUtil;
import yhao.infra.web.common.security.encrypt.EncryptCheckResult;

import jakarta.annotation.PostConstruct;
import javax.crypto.spec.SecretKeySpec;
import jakarta.servlet.http.HttpServletRequest;
import java.util.Map;
import java.util.UUID;

import static yhao.infra.web.YhaoInfraWebProperties.ENCRYPT_CACHE_BEAN_NAME;

/**
 * 提供AES秘钥存储，参数生成等应用层所需的一些工具方法
 *
 * @author yoara
 */
@Component("aesEncryptHelper")
@DependsOn(SpringBeanPicker.BEAN_NAME)
public class AESEncryptHelper extends AbstractEncryptHelper {
    private YhaoCache yhaoCache;

    /** 待校验的参数名，值为字符串，多个用","分开 **/
    public static final String AES_PARAM_ENCRYPTS = "AES_PARAM_ENCRYPTS";
    /** 是否仅MD5加密，值为boolean **/
    public static final String AES_PARAM_ONLYMD5 = "AES_PARAM_ONLYMD5";
    /** 加密后的字符串 **/
    public static final String AES_PARAM_ENCRYPTSTR = "AES_PARAM_ENCRYPTSTR";
    /** 存储的键值 **/
    public static final String AES_PARAM_KEY = "AES_PARAM_KEY";

    @PostConstruct
    private void init(){
        Environment env = SpringBeanPicker.getBean(Environment.class);
        String redisBeanName = env.getProperty(ENCRYPT_CACHE_BEAN_NAME);
        if(StringUtils.isNotBlank(redisBeanName)){
            yhaoCache = SpringBeanPicker.getBean(redisBeanName,YhaoCache.class);
        }else{
            yhaoCache = SpringBeanPicker.getBean(InfraCommonConstants.COMMON_CACHE_BEAN_NAME,YhaoCache.class);
        }
        if(yhaoCache==null){
            throw new IllegalStateException("=== no EncryptHelperCache found,please set 'basic.encrypt.cache.bean.name' ===");
        }
    }

    /**
     * 获取需要加密的参数串
     * @param request
     * @param form
     * @return
     */
    public Map<String,String> makeCheckMap(HttpServletRequest request, ValidationForm form){
        String paramAttrs = request.getParameter(AES_PARAM_ENCRYPTS);
        return makeCheckMap(paramAttrs,form);
    }

    /**
     * 将生成的AES秘钥存储至分布式缓存
     * @param keyByte 生成的加密秘钥数组
     * @param expire 存储超时时间,单位为秒，最多不超过30分钟
     * @return 缓存的key
     */
    public String putAESSecretKeySpec(byte[] keyByte,int expire){
        SecretKeySpec keySpec = AESUtil.getAESSecretKey(keyByte);
        if(expire>30*60){
            expire = 30*60;
        }
        String key = UUID.randomUUID().toString();
        yhaoCache.put(InfraCacheKey.CACHE_ENCRYPT_AES,key,keySpec,expire);
        return key;
    }

    @Override
    protected EncryptCheckResult checkSign(String sign, String paramMD5, String key, boolean onlyMD5){
        if(onlyMD5){
            return sign.equals(paramMD5)? EncryptCheckResult.MATCHED: EncryptCheckResult.MISMATCHED;
        }
        SecretKeySpec keySpec = (SecretKeySpec) yhaoCache.get(InfraCacheKey.CACHE_ENCRYPT_AES,key);
        if(keySpec==null){
            return EncryptCheckResult.INVALID_RSAKEY;
        }
        //将传递的sign解密成原始md5，与生成的md5比较
        try{
            String rsaSign = AESUtil.deCrypt(sign,keySpec,true).toUpperCase();
            if(rsaSign.length()>32){
                rsaSign = rsaSign.substring(rsaSign.length()-32);
            }
            return rsaSign.equals(paramMD5)? EncryptCheckResult.MATCHED: EncryptCheckResult.MISMATCHED;
        }catch (Exception e){
            log.error(e.getMessage(),e);
            return EncryptCheckResult.CHECK_FAIL;
        }
    }
}
